package com.didi.gulimall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.didi.gulimall.api.*;
import com.didi.gulimall.common.constant.*;
import com.didi.gulimall.common.entity.LocalMqEntity;
import com.didi.gulimall.common.exception.DuplicateFieldsException;
import com.didi.gulimall.common.to.MemberResponseVo;
import com.didi.gulimall.common.to.OrderBackVo;
import com.didi.gulimall.common.to.SkuInfoTO;
import com.didi.gulimall.common.to.WareOrderTaskDetaiTo;
import com.didi.gulimall.common.to.mq.SeckillOrderTo;
import com.didi.gulimall.common.utils.AmountCalculationUtils;
import com.didi.gulimall.common.utils.LogUtils;
import com.didi.gulimall.common.utils.R;
import com.didi.gulimall.common.vo.OrderItemVo;
import com.didi.gulimall.common.vo.OrderVo;
import com.didi.gulimall.mq.GuliRabbitTemplate;
import com.didi.gulimall.order.entity.OrderEntity;
import com.didi.gulimall.order.entity.OrderItemEntity;
import com.didi.gulimall.order.interceptor.LoginUserInterceptor;
import com.didi.gulimall.order.service.OrderItemService;
import com.didi.gulimall.order.service.OrderService;
import com.didi.gulimall.order.service.OrderWebService;
import com.didi.gulimall.order.to.OrderCreateTo;
import com.didi.gulimall.order.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


/**
 * @author 10626
 * TODO 远程调用判断 公共  改为消息
 */
@Slf4j
@Service
public class OrderWebServiceImpl implements OrderWebService {
    private ThreadLocal<OrderSubmitVo> confirmVoThreadLocal = new ThreadLocal<>();

    private ThreadLocal<OrderVo> orderVoThreadLocal = new ThreadLocal<>();

    private ThreadLocal<OrderCreateTo> orderTotalThreadLocal = new ThreadLocal<>();

    private ThreadLocal<Map<Long, OrderItemEntity>> orderItemMapThreadLocal = new ThreadLocal<>();

    @Autowired
    private MemberApiService memberApiService;

    @Autowired
    private CartApiService cartApiService;

    @Autowired
    private ThreadPoolExecutor executor;

    @Autowired
    private WareApiService wareApiService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private CouponApiService couponApiService;

    @Autowired
    private ProductApiService productApiService;

    @Autowired
    private OrderService orderService;

    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private GuliRabbitTemplate guliRabbitTemplate;

    @Autowired
    private SecKillApiService secKillApiService;

    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            // TODO 通过查询地区表
            R memberAddress = memberApiService.getMemberAddress(memberResponseVo.getId());
            if (memberAddress.getCode() != 0) {
                throw new DuplicateFieldsException(memberAddress.getData(SystemConstant.MSG, new TypeReference<String>() {
                }));
            }
            List<MemberAddressVo> address = memberAddress.getData("address", new TypeReference<List<MemberAddressVo>>() {
            });
            orderConfirmVo.setAddress(address);
        }, executor);
        // TODO  获取促销金额
        CompletableFuture<Void> itemsFuture = CompletableFuture.runAsync(() -> {
            // 1.购物项
            RequestContextHolder.setRequestAttributes(requestAttributes);
            R cartItems = cartApiService.memberList();
            if (cartItems.getCode() != 0) {
                throw new DuplicateFieldsException(cartItems.getData(SystemConstant.MSG, new TypeReference<String>() {
                }));
            }
            List<OrderItemVo> items = cartItems.getData("cartItems", new TypeReference<List<OrderItemVo>>() {
            });
            if (CollectionUtils.isNotEmpty(items)) {
                R hasstock = wareApiService.getSkusHasStockbByCartItem(items);
                if (hasstock.getCode() != 0) {
                    throw new DuplicateFieldsException(hasstock.getData(SystemConstant.MSG, new TypeReference<String>() {
                    }));
                }
                // TODO 显示剩余的件数
                Map<Long, Boolean> map = hasstock.getData("hasstock", new TypeReference<Map<Long, Boolean>>() {
                });
                List<OrderItemVo> outStock = new ArrayList<>();
                Iterator<OrderItemVo> iterator = items.iterator();
                while (iterator.hasNext()) {
                    OrderItemVo next = iterator.next();
                    if (!map.containsKey(next.getSkuId()) || !map.get(next.getSkuId())) {
                        iterator.remove(); //注意这里调用的是集合的方法
                        outStock.add(next);
                    }
                }
                orderConfirmVo.setSoldOutItems(outStock);
            }
            OrderVo data = getOrderVo(memberResponseVo.getLevelId(), items);
            BeanUtils.copyProperties(data, orderConfirmVo);
        }, executor);

        String token = UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberResponseVo.getId(), token);
        // 用户积分
        orderConfirmVo.setOrderToken(token);
        Integer integration = memberResponseVo.getIntegration();
        orderConfirmVo.setIntegration(integration);
        CompletableFuture.allOf(addressFuture, itemsFuture).get();
        orderConfirmVo.setPaymentTypes(OrderConstant.BUY);
        return orderConfirmVo;
    }

    @Override
    public OrderConfirmVo seckillConfirmOrder() throws ExecutionException, InterruptedException {
        MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            // TODO 通过查询地区表
            R memberAddress = memberApiService.getMemberAddress(memberResponseVo.getId());
            if (memberAddress.getCode() != 0) {
                throw new DuplicateFieldsException(memberAddress.getData(SystemConstant.MSG, new TypeReference<String>() {
                }));
            }
            List<MemberAddressVo> address = memberAddress.getData("address", new TypeReference<List<MemberAddressVo>>() {
            });
            orderConfirmVo.setAddress(address);
        }, executor);

        CompletableFuture<Void> itemsFuture = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<OrderItemVo> collect = getSecKillOrderItemVos();
            orderConfirmVo.setItems(collect);
        }, executor);

        String token = UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberResponseVo.getId(), token);
        // 用户积分
        orderConfirmVo.setOrderToken(token);
        Integer integration = memberResponseVo.getIntegration();
        orderConfirmVo.setIntegration(integration);

        CompletableFuture.allOf(addressFuture, itemsFuture).get();
        orderConfirmVo.setPaymentTypes(OrderConstant.SECKILL);
        setOrderConfirmDetail(orderConfirmVo, orderConfirmVo.getItems());

        return orderConfirmVo;
    }

    private List<OrderItemVo> getSecKillOrderItemVos() {
        R seckill = secKillApiService.getSecKillMemberList();
        if (seckill.getCode() != 0) {
            throw new DuplicateFieldsException(seckill.getMsg());
        }
        List<SeckillOrderTo> data = seckill.getData(new TypeReference<List<SeckillOrderTo>>() {
        });
        return data.stream().map(seckillOrderTo -> {
            SkuInfoTO skuInfoTO = seckillOrderTo.getSkuInfoTO();
            OrderItemVo orderItemVo = new OrderItemVo();
            orderItemVo.setSkuDefaultImg(skuInfoTO.getSkuDefaultImg());
            orderItemVo.setSkuId(seckillOrderTo.getSkuId());
            orderItemVo.setSkuTitle(skuInfoTO.getSkuTitle());
            orderItemVo.setCount(seckillOrderTo.getNum());
            orderItemVo.setPrice(seckillOrderTo.getSeckillPrice());
            orderItemVo.setFullDiscount(BigDecimal.ZERO);
            // TODO
//          orderItemVo.setSkuAttr(skuInfoTO.getSkuTitle());
            orderItemVo.setMemberTieredPrice(BigDecimal.ZERO);
            orderItemVo.setPromotionAmount(BigDecimal.ZERO);
            orderItemVo.setTotalPrice(seckillOrderTo.getSeckillPrice().multiply(new BigDecimal(seckillOrderTo.getNum().toString())));
            return orderItemVo;
        }).collect(Collectors.toList());
    }

    @Override
    public String submitSeckillOrder(OrderSubmitVo orderSubmitVo) {
        confirmVoThreadLocal.set(orderSubmitVo);
        MemberResponseVo member = LoginUserInterceptor.loginUser.get();
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        String token = OrderConstant.USER_ORDER_TOKEN_PREFIX + member.getId();
        Long execute = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Collections.singletonList(token), orderSubmitVo.getOrderToken());
        if (execute == null || execute != 1L) {
            throw new DuplicateFieldsException(BizCodeEnume.SECURITY_CHECK.getMsg());
        }
        // 校验用户积分和成长值是否合法
        Integer memberIntegration = member.getIntegration();
        Integer memberGrowth = member.getGrowth();
        Integer growth = orderSubmitVo.getGrowth();
        Integer integration = orderSubmitVo.getIntegration();
        if (growth > memberGrowth || integration > memberIntegration) {
            throw new DuplicateFieldsException(BizCodeEnume.ILLEGAL_USE_OF_POINTS.getMsg());
        }
        //令牌验证成功
        OrderCreateTo order = createSecKillOrder();
        BigDecimal payPrice = orderSubmitVo.getPayPrice();
        BigDecimal totalPricePayable = order.getOrder().getPayAmount();
        // 校验金额
        log.info("系统计算金额:{}--用户携带金额:{}", totalPricePayable, payPrice);
        if ((Math.abs(totalPricePayable.subtract(payPrice).intValue()) > 0.01)) {
            throw new DuplicateFieldsException(BizCodeEnume.AMOUNT_VERIFICATION.getMsg());
        }
        OrderVo orderVo = null;
        try {
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            CompletableFuture<Void> orderFuture = CompletableFuture.runAsync(() -> {
                saveOrder(order);
            }, executor);

            //下单：去创建订单，验令牌，验价格，锁库存...
            //1、创建订单，订单项等信息

            CompletableFuture<Void> memberFuture = CompletableFuture.runAsync(() -> {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                MemberOrderPointsVo memberOrderPointsVo = new MemberOrderPointsVo();
                memberOrderPointsVo.setGrowth(orderSubmitVo.getGrowth());
                memberOrderPointsVo.setIntegration(orderSubmitVo.getIntegration());
                memberOrderPointsVo.setMemberId(member.getId());
                memberOrderPointsVo.setOrderSn(order.getOrder().getOrderSn());

                R r = memberApiService.subjectPoint(null);
                if (r.getCode() != 0) {
                    throw new DuplicateFieldsException(r.getMsg());
                }
            }, executor);

            orderVo = orderVoThreadLocal.get();
            order.getOrder().setItemVos(orderVo.getItems());

            CompletableFuture.allOf(memberFuture);
            // TODO 延时队列
            OrderBackVo orderBackVo = new OrderBackVo();
            orderBackVo.setOrderSn(order.getOrder().getOrderSn());
            orderBackVo.setHasStock(WareConstant.LOCK_INVENTORY);
            orderBackVo.setMemberId(member.getId());
            orderBackVo.setOrderItemVos(orderVo.getItems());

            LocalMqEntity localMqEntity = new LocalMqEntity(UUID.randomUUID().toString(), "order-event-exchange", "order.create.order", JSON.toJSONString(orderBackVo), "com.didi.gulimall.common.to.OrderBackVo");
            guliRabbitTemplate.sendConvertAndSend(localMqEntity);
            return order.getOrder().getOrderSn();
        } catch (Exception e) {
            log.error("错误信息:{}", e.getMessage());
            log.error(LogUtils.getStackTraceInfo(e));

            OrderBackVo orderBackVo = new OrderBackVo();
            orderBackVo.setOrderSn(order.getOrder().getOrderSn());
            orderBackVo.setMemberId(member.getId());
            orderBackVo.setHasStock(WareConstant.LOCK_INVENTORY);
            if (orderVo != null) {
                orderBackVo.setOrderItemVos(orderVo.getItems());
            }
            LocalMqEntity localMqEntity = new LocalMqEntity(UUID.randomUUID().toString(), "order-event-exchange", "order.release.#", JSON.toJSONString(orderBackVo), "com.didi.gulimall.common.to.OrderBackVo");
            guliRabbitTemplate.sendConvertAndSend(localMqEntity);
            throw new DuplicateFieldsException(e.getMessage());
        }
    }

    private void setOrderConfirmDetail(OrderConfirmVo orderConfirmVo, List<OrderItemVo> items) {
        Integer totalNum = 0;
        BigDecimal totalTotalAmount = BigDecimal.ZERO;
        BigDecimal totalTotalFullDiscount = BigDecimal.ZERO;
        BigDecimal totalMemberTieredPrice = BigDecimal.ZERO;
        BigDecimal totalPricePayable = BigDecimal.ZERO;
        BigDecimal totalPromotionAmount = BigDecimal.ZERO;
        for (OrderItemVo item : items) {
            totalNum += item.getCount();
            totalTotalAmount = totalTotalAmount.add(item.getTotalPrice());
            if (item.getFullDiscount() != null) {
                totalTotalFullDiscount = totalTotalFullDiscount.add(item.getFullDiscount());
            }
            if (item.getMemberTieredPrice() != null) {
                totalMemberTieredPrice = totalMemberTieredPrice.add(item.getMemberTieredPrice());
            }
            if (item.getPromotionAmount() != null) {
                totalPromotionAmount = totalPromotionAmount.add(item.getPromotionAmount());
            }
            totalPricePayable = totalTotalAmount.subtract(totalTotalFullDiscount).subtract(totalMemberTieredPrice).subtract(totalPromotionAmount);
        }
        orderConfirmVo.setCountNum(totalNum);
        orderConfirmVo.setCountType(items.size());
        orderConfirmVo.setTotalAmount(totalTotalAmount);
        orderConfirmVo.setTotalFullDiscount(totalTotalFullDiscount);
        orderConfirmVo.setTotalMemberTieredPrice(totalMemberTieredPrice);
        orderConfirmVo.setTotalPricePayable(totalPricePayable);
        orderConfirmVo.setTotalPromotionAmount(totalPromotionAmount);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String submitOrder(OrderSubmitVo orderSubmitVo) throws ExecutionException, InterruptedException {
        confirmVoThreadLocal.set(orderSubmitVo);
        MemberResponseVo member = LoginUserInterceptor.loginUser.get();
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        String token = OrderConstant.USER_ORDER_TOKEN_PREFIX + member.getId();
        Long execute = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Collections.singletonList(token), orderSubmitVo.getOrderToken());
        if (execute == null || execute != 1L) {
            throw new DuplicateFieldsException(BizCodeEnume.SECURITY_CHECK.getMsg());
        }
        // 校验用户积分和成长值是否合法
        Integer memberIntegration = member.getIntegration();
        Integer memberGrowth = member.getGrowth();
        Integer growth = orderSubmitVo.getGrowth();
        Integer integration = orderSubmitVo.getIntegration();
        if (growth > memberGrowth || integration > memberIntegration) {
            throw new DuplicateFieldsException(BizCodeEnume.ILLEGAL_USE_OF_POINTS.getMsg());
        }
        //令牌验证成功
        OrderCreateTo order = createOrder();
        BigDecimal payPrice = orderSubmitVo.getPayPrice();

        BigDecimal totalPricePayable = order.getOrder().getPayAmount();
        // 校验金额
        log.info("系统计算金额:{}--用户携带金额:{}", totalPricePayable, payPrice);
        if ((Math.abs(totalPricePayable.subtract(payPrice).intValue()) > 0.01)) {
            throw new DuplicateFieldsException(BizCodeEnume.AMOUNT_VERIFICATION.getMsg());
        }
        List<MemberOrderPointsVo> memberOrderPointsVos = new ArrayList<>();
        List<OrderBackVo> orderBackVos = new ArrayList<>();
        try {
            R ware = wareApiService.deductionInventory(order.getOrder());
            if (ware.getCode() != 0) {
                throw new DuplicateFieldsException(ware.getData(SystemConstant.MSG, new TypeReference<String>() {
                }));
            }
            Map<Long, List<WareOrderTaskDetaiTo>> res = ware.getData(new TypeReference<Map<Long, List<WareOrderTaskDetaiTo>>>() {
            });
            if (res == null) {
                throw new DuplicateFieldsException(ware.getData(SystemConstant.MSG, new TypeReference<String>() {
                }));
            }
            log.info("库存返回数据：{}", JSON.toJSONString(res));
            Map<String, OrderVo> spiltOrders = spiltOrder(res);
            log.info("订单拆单数据：{}", JSON.toJSONString(spiltOrders));
            spiltOrderList(memberOrderPointsVos, orderBackVos, spiltOrders);
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            CompletableFuture<Void> memberFuture = CompletableFuture.runAsync(() -> {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                R r = memberApiService.subjectPoint(memberOrderPointsVos);
                if (r.getCode() != 0) {
                    throw new DuplicateFieldsException(r.getMsg());
                }
            }, executor);

            List<OrderItemVo> itemVos = order.getOrder().getItemVos();
            CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                // 购物车
                R cart = cartApiService.orderRemoveItem(itemVos);
                log.info("远程调用购物车服务情况:{}", JSON.toJSONString(cart));
            }, executor);
            CompletableFuture.allOf(cartFuture, memberFuture).get();
            remove();
            // TODO 延时队列
            for (OrderBackVo orderBackVo : orderBackVos) {
                LocalMqEntity localMqEntity = new LocalMqEntity(UUID.randomUUID().toString(), "order-event-exchange", "order.create.order", JSON.toJSONString(orderBackVo), "com.didi.gulimall.common.to.OrderBackVo");
                guliRabbitTemplate.sendConvertAndSend(localMqEntity);
            }
            return order.getOrder().getOrderSn();
        } catch (Exception e) {
            log.error("错误信息:{}0", e.getMessage());
            log.error(LogUtils.getStackTraceInfo(e));
            if (CollectionUtils.isNotEmpty(orderBackVos)) {
                for (OrderBackVo orderBackVo : orderBackVos) {
                    LocalMqEntity localMqEntity = new LocalMqEntity(UUID.randomUUID().toString(), "order-event-exchange", "order.release.#", JSON.toJSONString(orderBackVo), "com.didi.gulimall.common.to.OrderBackVo");
                    guliRabbitTemplate.sendConvertAndSend(localMqEntity);
                }
            }
            throw new DuplicateFieldsException(e.getMessage());
        }
    }

    private void remove() {
        confirmVoThreadLocal.remove();
        orderVoThreadLocal.remove();
        orderTotalThreadLocal.remove();
        orderItemMapThreadLocal.remove();
    }

    private void spiltOrderList(List<MemberOrderPointsVo> memberOrderPointsVos, List<OrderBackVo> orderBackVos, Map<String, OrderVo> spiltOrders) {
        Set<String> keys = spiltOrders.keySet();
        MemberResponseVo member = LoginUserInterceptor.loginUser.get();
        for (String key : keys) {
            OrderVo orderSpilt = spiltOrders.get(key);
            MemberOrderPointsVo memberOrderPointsVo = new MemberOrderPointsVo();
            memberOrderPointsVo.setMemberId(member.getId());
            memberOrderPointsVo.setOrderSn(orderSpilt.getOrderSn());
            memberOrderPointsVo.setGrowth(orderSpilt.getGrowth());
            memberOrderPointsVo.setIntegration(orderSpilt.getIntegration());
            memberOrderPointsVos.add(memberOrderPointsVo);

            OrderBackVo orderBackVo = new OrderBackVo();
            orderBackVo.setOrderItemVos(orderSpilt.getItems());
            orderBackVo.setMemberId(member.getId());
            orderBackVo.setHasStock(WareConstant.LOCK_INVENTORY);
            orderBackVo.setOrderSn(orderSpilt.getOrderSn());
            orderBackVo.setGrowth(orderSpilt.getGrowth());
            orderBackVos.add(orderBackVo);
        }
    }

    private Map<String, OrderVo> spiltOrder(Map<Long, List<WareOrderTaskDetaiTo>> res) {
        Map<String, OrderVo> map = new HashMap<>();
        Map<Long, OrderItemEntity> orderItemMap = orderItemMapThreadLocal.get();
        Set<Long> longs = res.keySet();
        log.info("共有订单数：{}", res.size());
        for (Long wareId : longs) {
            List<OrderItemVo> orderItemList = new ArrayList<>();
            List<OrderItemEntity> orderItemEntities = new ArrayList<>();
            List<WareOrderTaskDetaiTo> wareOrderTasks = res.get(wareId);
            OrderCreateTo order = orderTotalThreadLocal.get();
            OrderEntity orderTotalEntity = order.getOrder();
            BigDecimal totalRealAmount = BigDecimal.ZERO;
            BigDecimal totalAccount = BigDecimal.ZERO;
            String orderSn = "";
            Integer totalGiftGrowth = 0;
            Integer totalGiftIntegration = 0;
            Integer useGrowth = 0;
            Integer useIntegration = 0;
            BigDecimal totalFullDiscount = BigDecimal.ZERO;
            BigDecimal totalMemberTieredPrice = BigDecimal.ZERO;
            BigDecimal totalDiscountAmount = BigDecimal.ZERO;
            BigDecimal totalPromotionAmount = BigDecimal.ZERO;
            BigDecimal totalIntegrationAmount = BigDecimal.ZERO;
            BigDecimal totalGrowthValueAmount = BigDecimal.ZERO;
            for (WareOrderTaskDetaiTo wareOrder : wareOrderTasks) {
                OrderItemEntity orderItemEntity = orderItemMap.get(wareOrder.getSkuId());
                orderSn = wareOrder.getOrderSn();
                log.info("当前订单号：{}", orderSn);
                orderItemEntity.setOrderSn(orderSn);
                OrderItemVo orderItemVo = getOrderItemVo(orderItemEntity);
                orderItemList.add(orderItemVo);
                totalAccount = totalAccount.add(orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity())));
                useGrowth += orderItemEntity.getUseGrowthValue();
                useIntegration += orderItemEntity.getUseIntegration();
                totalFullDiscount = totalFullDiscount.add(orderItemEntity.getFullDiscount());
                BigDecimal freeMemberTieredPrice = (orderItemEntity.getSkuPrice().subtract(orderItemEntity.getMemberTieredPrice())).multiply(new BigDecimal(orderItemEntity.getSkuQuantity()));
                totalMemberTieredPrice = totalMemberTieredPrice.add(freeMemberTieredPrice);
                totalPromotionAmount = totalPromotionAmount.add(orderItemEntity.getPromotionAmount());
                totalIntegrationAmount = totalIntegrationAmount.add(orderItemEntity.getIntegrationAmount());
                totalGrowthValueAmount = totalGrowthValueAmount.add(orderItemEntity.getGrowthValueAmount());
                totalGiftGrowth += orderItemEntity.getGiftGrowth();
                totalGiftIntegration += orderItemEntity.getGiftIntegration();
                orderItemMap.put(wareOrder.getSkuId(), orderItemEntity);
                totalRealAmount = totalRealAmount.add(orderItemEntity.getRealAmount());
                orderItemEntities.add(orderItemEntity);
            }
            OrderEntity orderEntity = couponRatioMatching(orderTotalEntity, totalRealAmount);
            orderEntity.setPayAmount(totalRealAmount);
            orderEntity.setTotalAmount(totalAccount);
            orderEntity.setTotalOrderSn(orderTotalEntity.getOrderSn());
            orderEntity.setOrderSn(orderSn);
            orderEntity.setFullDiscount(totalFullDiscount);
            orderEntity.setMemberTieredPrice(totalMemberTieredPrice);
            orderEntity.setPromotionAmount(totalPromotionAmount);
            orderEntity.setIntegrationAmount(totalIntegrationAmount);
            orderEntity.setGrowthValueAmount(totalGrowthValueAmount);
            orderEntity.setGrowth(totalGiftGrowth);
            orderEntity.setIntegration(totalGiftIntegration);
            orderEntity.setUseIntegration(useIntegration);
            orderEntity.setUseGrowthValue(useGrowth);
            orderEntity.setDiscountAmount(totalDiscountAmount);
            OrderVo orderVo = getSpiltOrderVo(orderEntity);
            orderVo.setItems(orderItemList);
            map.put(orderEntity.getOrderSn(), orderVo);
            saveSplitOrder(orderEntity, orderItemEntities);
        }
        return map;
    }

    private OrderVo getSpiltOrderVo(OrderEntity orderEntity) {
        OrderVo orderVo = new OrderVo();
        BeanUtils.copyProperties(orderEntity, orderVo);
        return orderVo;
    }

    private OrderItemVo getOrderItemVo(OrderItemEntity orderItemEntity) {
        OrderItemVo orderItemVo = new OrderItemVo();
        orderItemVo.setCount(orderItemEntity.getSkuQuantity());
        BeanUtils.copyProperties(orderItemEntity, orderItemVo);
        return orderItemVo;
    }

    private void saveSplitOrder(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntities) {
        orderService.save(orderEntity);
        orderItemService.saveBatch(orderItemEntities);
    }

    private OrderEntity couponRatioMatching(OrderEntity orderTatalEntity, BigDecimal realAmount) {
        OrderEntity orderEntity = new OrderEntity();
        BigDecimal couponAmount = orderTatalEntity.getCouponAmount();
        BeanUtils.copyProperties(orderTatalEntity, orderEntity);
        if (couponAmount != null) {
            BigDecimal couppon = realAmount.divide(orderTatalEntity.getPayAmount(), 2, BigDecimal.ROUND_HALF_UP);
            orderEntity.setCouponAmount(couponAmount.multiply(couppon));
        }
        return orderEntity;
    }

    @Override
    public PayOrderVo getOrderVo(String totalOrderSn) {
        PayOrderVo payOrderVo = new PayOrderVo();
        List<OrderEntity> orderVo = orderService.getOrderVo(totalOrderSn);
        if (CollectionUtils.isEmpty(orderVo)) {
            return null;
        }
        BigDecimal totalAccount = BigDecimal.ZERO;
        for (OrderEntity orderEntity : orderVo) {
            totalAccount = totalAccount.add(orderEntity.getPayAmount());
        }
        OrderEntity orderEntity = orderVo.get(0);
        BeanUtils.copyProperties(orderEntity, payOrderVo);
        payOrderVo.setOrderVos(orderVo);
        payOrderVo.setTotalPayAccount(totalAccount);
        payOrderVo.setOrderSn(totalOrderSn);
        return payOrderVo;
    }

    private void saveOrder(OrderCreateTo order) {
        OrderEntity orderEntity = order.getOrder();
        List<OrderItemEntity> orderItems = order.getOrderItems();
        orderService.save(orderEntity);
        orderItemService.saveBatch(orderItems);
    }

    private OrderCreateTo createOrder() throws ExecutionException, InterruptedException {
        MemberResponseVo member = LoginUserInterceptor.loginUser.get();

        OrderCreateTo orderCreateTo = new OrderCreateTo();
        String orderSn = IdWorker.getTimeId();
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        CompletableFuture<OrderVo> cartItems = CompletableFuture.supplyAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            R cartItemsResult = cartApiService.memberListNoUpdateProduct();
            if (cartItemsResult.getCode() != 0) {
                throw new DuplicateFieldsException(cartItemsResult.getData(SystemConstant.MSG, new TypeReference<String>() {
                }));
            }
            List<OrderItemVo> orderItems = cartItemsResult.getData("cartItems", new TypeReference<List<OrderItemVo>>() {
            });
            if (CollectionUtils.isEmpty(orderItems)) {
                throw new DuplicateFieldsException(BizCodeEnume.CHOOSE_AT_LEAST_ONE_PRODUCT.getMsg());
            }
            return getOrderVo(member.getLevelId(), orderItems);
        }, executor);

        CompletableFuture<OrderEntity> orderEntityCompletableFuture = CompletableFuture.supplyAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            return buildOrder(orderSn);
        }, executor);
        OrderVo data = cartItems.get();
        OrderEntity orderEntity = orderEntityCompletableFuture.get();
        orderVoThreadLocal.set(data);

        List<OrderItemEntity> orderItemEntities = buildOrderItems(orderSn, data.getItems());
        orderEntity.setItemVos(data.getItems());
        computePrice(orderEntity);
        orderCreateTo.setOrder(orderEntity);
        orderCreateTo.setOrderItems(orderItemEntities);
        orderTotalThreadLocal.set(orderCreateTo);
        return orderCreateTo;
    }

    private OrderCreateTo createSecKillOrder() {
        OrderSubmitVo orderSubmitVo = confirmVoThreadLocal.get();
        OrderCreateTo orderCreateTo = new OrderCreateTo();
        String orderSn = orderSubmitVo.getOrderSn();
        List<OrderItemVo> secKillOrderItemVos = getSecKillOrderItemVos();
        if (CollectionUtils.isEmpty(secKillOrderItemVos)) {
            throw new DuplicateFieldsException(BizCodeEnume.CHOOSE_AT_LEAST_ONE_PRODUCT.getMsg());
        }
        // 拆单

        OrderEntity orderEntity = buildOrder(orderSn);
        List<OrderItemEntity> orderItemEntities = buildOrderItems(orderSn, secKillOrderItemVos);
        // 校验金额 设置总成长值
        computePrice(orderEntity);
        orderCreateTo.setOrder(orderEntity);
        orderCreateTo.setOrderItems(orderItemEntities);
        return orderCreateTo;
    }

    private OrderVo getOrderVo(Long levelId, List<OrderItemVo> cartItems) {
        R r = couponApiService.getPromotionAmount(levelId, cartItems);
        // 优惠信息
        if (r.getCode() != 0) {
            throw new DuplicateFieldsException(r.getData(SystemConstant.MSG, new TypeReference<String>() {
            }));
        }
        return r.getData(new TypeReference<OrderVo>() {
        });
    }

    private void computePrice(OrderEntity orderEntity) {
        OrderVo orderVo = orderVoThreadLocal.get();

        BigDecimal totalPricePayable = orderVo.getTotalPricePayable();
        totalPricePayable = totalPricePayable.add(new BigDecimal(String.valueOf(orderEntity.getFreightAmount())));

        orderEntity.setPayAmount(totalPricePayable);
    }

    private List<OrderItemEntity> buildOrderItems(String orderSn, List<OrderItemVo> cartItems) {
        List<OrderItemEntity> orderItemEntities = new ArrayList<>();
        for (OrderItemVo cartItem : cartItems) {
            OrderItemEntity orderItem = buildOrderItem(orderSn, cartItem);
            Map<Long, OrderItemEntity> orderItemMap = new HashMap<>();
            orderItemMapThreadLocal.set(orderItemMap);
            orderItemMap.put(cartItem.getSkuId(), orderItem);
            orderItemEntities.add(orderItem);
        }

        return orderItemEntities;
    }

    private OrderItemEntity buildOrderItem(String orderSn, OrderItemVo cartItem) {
        // TODO 用户积分 优惠劵     // TODO 有的商品不能使用金币扣减
        R info = productApiService.info(cartItem.getSkuId());
        if (info.getCode() != 0) {
            throw new DuplicateFieldsException(info.getData(SystemConstant.MSG, new TypeReference<String>() {
            }));
        }
        SkuInfoTO skuInfo = info.getData("skuInfo", new TypeReference<SkuInfoTO>() {
        });
        OrderItemEntity orderItem = new OrderItemEntity();
        // 商品信息
        orderItem.setOrderSn(orderSn);
        orderItem.setSkuId(skuInfo.getSkuId());
        orderItem.setSkuName(skuInfo.getSkuName());
        orderItem.setSpuId(skuInfo.getSpuId());
        orderItem.setSpuName(skuInfo.getSkuTitle());
        orderItem.setSpuName(skuInfo.getSkuName());
        orderItem.setSpuPic(skuInfo.getSkuDefaultImg());
        orderItem.setSkuPic(skuInfo.getSkuDefaultImg());
        orderItem.setCategoryId(skuInfo.getCatalogId());
        orderItem.setSpuBrand(skuInfo.getBrandId());
        orderItem.setSpuId(skuInfo.getSpuId());
        orderItem.setSkuPrice(skuInfo.getPrice());
        orderItem.setSkuQuantity(cartItem.getCount());
        orderItem.setSkuAttrsVals(String.join(";", cartItem.getSkuAttr()));
        // 优惠信息 不包括卡卷 TODO 用户使用成长积分
        OrderSubmitVo orderSubmitVo = confirmVoThreadLocal.get();
        if (orderSubmitVo.getUseGrowthValue() < cartItem.getGrowth()) {
            orderSubmitVo.setUseGrowthValue(0);
        } else {
            orderItem.setUseGrowthValue(cartItem.getGrowth());
        }
        if (orderSubmitVo.getUseIntegration() < cartItem.getIntegration()) {
            orderSubmitVo.setUseIntegration(0);
        } else {
            orderItem.setUseIntegration(cartItem.getIntegration());
        }
        orderItem.setIntegrationAmount(AmountCalculationUtils.integrationConversion(orderItem.getUseIntegration()));
        orderItem.setGrowthValueAmount(AmountCalculationUtils.growthValueConversion(orderItem.getUseIntegration()));
        // 赠送信息
        orderItem.setGiftIntegration(cartItem.getGiveIntegration());
        orderItem.setGiftGrowth(cartItem.getGiveGrowth());
        // 计算支付总价  总价减各种优惠

        BigDecimal subtract = cartItem.getTotalPrice();
        subtract = subtract.subtract(orderItem.getGrowthValueAmount()).subtract(orderItem.getIntegrationAmount());
        if (cartItem.getFullDiscount() != null) {
            orderItem.setFullDiscount(cartItem.getFullDiscount());
            subtract = subtract.subtract(cartItem.getFullDiscount());
        }
        if (cartItem.getMemberTieredPrice() != null) {
            orderItem.setMemberTieredPrice(cartItem.getMemberTieredPrice());
            BigDecimal multiply = (orderItem.getSkuPrice().subtract(cartItem.getMemberTieredPrice())).multiply(new BigDecimal(cartItem.getCount()));
            subtract = subtract.subtract(multiply);
        }
        if (cartItem.getPromotionAmount() != null) {
            BigDecimal promotionAmount = cartItem.getTotalPrice().subtract(cartItem.getPromotionAmount());
            orderItem.setPromotionAmount(cartItem.getPromotionAmount());
            subtract = subtract.subtract(promotionAmount);
        }
        orderItem.setRealAmount(subtract);
        return orderItem;
    }


    private OrderEntity buildOrder(String orderSn) {
        MemberResponseVo member = LoginUserInterceptor.loginUser.get();
        OrderSubmitVo orderSubmitVo = confirmVoThreadLocal.get();

        R fare = wareApiService.getFare(orderSubmitVo.getAddrId());
        if (fare.getCode() != 0) {
            throw new DuplicateFieldsException(fare.getMsg());
        }
        FareVo data = fare.getData(new TypeReference<FareVo>() {
        });
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderTime(new Date());
        orderEntity.setFreightAmount(data.getFare());
        orderEntity.setOrderSn(orderSn);
        orderEntity.setMemberId(member.getId());
        orderEntity.setSourceType(OrderConstant.PC_ORDER);
        orderEntity.setStateDescription(OrderStatusEnum.CREATE_NEW.getMsg());
        orderEntity.setMemberUsername(member.getNickname());
        MemberAddressVo address = data.getAddress();
        orderEntity.setReceiverName(address.getName());
        orderEntity.setReceiverPhone(address.getPhone());
        orderEntity.setReceiverPostCode(address.getPostCode());
        orderEntity.setReceiverProvince(address.getProvince());
        orderEntity.setReceiverCity(address.getCity());
        orderEntity.setReceiverRegion(address.getRegion());
        orderEntity.setReceiverDetailAddress(address.getDetailAddress());
        orderEntity.setNote(orderSubmitVo.getNote());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setDeleteStatus(0);
        orderEntity.setConfirmStatus(0);

        return orderEntity;
    }
}
